﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "IGnufPropertyWidget.h"
#include "ComponentEditorGnufUI.h"
#include "ElementId.h"
#include "RuntimeId.h"
#include "UniIdentity.h"
#include "GnufArrangementAreaContent.h"
#include "GnufItemEvents.h"
#include "IParameter.h"
#include "IElementParameterGroupOverride.h"
#include "IDocument.h"

namespace gcmp
{
    class IUiView;
    class IUserTransaction;
    class IParameterValueStorage;
    class IUiViewEvent;
    class IUiDocumentEvent;
    class IUiViewEventHandler;
    class ISelectionChangeEventHandler;
    class IUiDocumentEventHandler;
    class IElementsChangeEventHandler;

    typedef std::tuple<Int64, std::wstring, std::wstring> ElementTypeInfo;
    typedef std::tuple<ElementTypeInfo, std::vector<ElementTypeInfo>> ElementTypeGroup;
    typedef std::vector<ElementTypeGroup> ElementTypeGroups;
}

namespace gnuf {
    class Pallet;
    class NameValuePairBlock;
    class Item;
    class List;
    class Text;
    class Check;
    class WidgetAgent;
    class NameValuePairs;
    class WidgetAgent;
    class NameValuePairs;
}

namespace gfam
{
    class QtNameFamilyType;
    class QtGnufPropertyPallet;
    class IGnufPropertyPanelEvent;
    class QtEditFamilyType;

    class PropertyNameValuePairsComponent : public gnuf::IItemEventHandler
    {
    public:
        virtual~PropertyNameValuePairsComponent() {}
        //刷新属性面板 -- 功能等同于Bimmake 的UpdatePropertyTable函数
        virtual void BuildNameValuePairs(bool rebuild = true, bool fromElements = true);
        // 从属性表界面修改属性项目更新到Element
        virtual bool UpdatePropertyItemToElement(gnuf::Item* pItem) = 0;
        //应用属性Item的改动到DB层（commit）
        virtual bool UpdatePropertyTableToElement() = 0;
        void SetNameValuePairs(gnuf::NameValuePairs* pNameValuePairs) { m_pNameValuePairs = pNameValuePairs; }
        void SetDocument(gcmp::IDocument* pDoc);
        void SetElementIds(const std::vector<gcmp::ElementId> ids) { m_elementIds = ids; }
    protected:
        void GetCommonParameters(gcmp::IDocument* pDoc,
            const gcmp::IElement* pEle,
            const std::vector<gcmp::ElementId>& selElements, /*[out]*/
            std::vector<gcmp::OwnerPtr<gcmp::IParameter>>& params, /*[out]*/
            std::vector<bool>& paramMultiVals);

        gnuf::NameValuePairs* m_pNameValuePairs;
        gcmp::WeakPtr<gcmp::IDocument> m_wpDoc;
        std::vector<gcmp::ElementId> m_elementIds;
        gcmp::ElementId m_tmpPreSetElementId;        // 用于预设参数的临时构件
        gcmp::GroupNameParametersArray m_parametersArray;
    };

    class InstanceNameValuePairsComponent final : public PropertyNameValuePairsComponent
    {
        friend class FamilyTypeIntegratedComponent;
        friend class FamilyNameValuePairsComponent;
    public:
        InstanceNameValuePairsComponent();
        bool IsConnected() const { return m_connected; }
        void SetNeedUpdate(bool need) { m_isNeedUpdate = need; }
        bool IsNeedUpdate() const { return m_isNeedUpdate; }
        bool RefreshByParameterValueStorage(gcmp::IParameterValueStorage *pCurrentParamValue, int parameterDefinitionId);
        void SetPropertyTableCanBeUpdated(bool canOrNot) { m_canPropertyTableBeUpdated = canOrNot; }
        //鼠标移开属性面板区域的自动应用更改动作
        void onApply();
        void onPropertyValueChanged(gnuf::Item*);
        void OnPropertyBindButtonClicked(gnuf::Item*);
        void OnPropertyDropDownButtonClicked(gnuf::Item*);

    public:
        virtual void onCurrentIndexChanged(gnuf::List*, int) override;
        virtual void onTextChanged(gnuf::Text*) override;
        virtual void onCheckStatusChanged(gnuf::Check*, bool) override;
        virtual void onColorChanged(gnuf::ColorControl*) override;
        virtual void onClicked(gnuf::Item*) override;
        virtual bool UpdatePropertyItemToElement(gnuf::Item* pItem);
        virtual bool UpdatePropertyTableToElement();

        void quitTransaction();

        // 检查环形阵列参数合法性
        bool CheckRadialArrayParameter(int paramDefId, int newParamDefId, std::wstring* errorMessage);

        // 检查线性阵列参数合法性
        bool CheckLinearArrayParameter(int paramDefId, int newParamDefId, std::wstring* errorMessage);

        // 检查富文本参数的合法性
        bool CheckRichTextParameter(int paramDefId, int newParamDefId, std::wstring* errorMessage);

    private:
        void Connect(bool is) { m_connected = is; }
        bool UpdatePropertyItemToElementParameter(const gnuf::Item* pItem, gcmp::IElement* pElement, const std::vector<gcmp::ElementId>& selElements, bool& bCommitAlways, bool& error);
        void UpdatePresetParamValue(bool bParameterChanged, gcmp::IElement* pElement, gcmp::IParameter* param) const;
        void UpdateViewVisual() const;
        //做光标离开检查事件处理
        void CheckLeaveEventHandler();

        bool m_connected;
        bool m_isNeedUpdate;// 是否需要更新(用于连续修改延时更新）
        bool m_isPropertyValueChanged;
        bool m_canPropertyTableBeUpdated;//材质相关使用场景
        bool m_bTransactionIsCommitting;
        gcmp::OwnerPtr<gcmp::IUserTransaction> m_upUserTransaction;
    };

    class FamilyTypeNameComponentParent
    {
    public:
        virtual~FamilyTypeNameComponentParent() {}
        virtual void onFamilyTypeNameChanged(const std::wstring&) = 0;
    };

    class FamilyTypeNameComponent
    {
    public:
        FamilyTypeNameComponent(FamilyTypeNameComponentParent* pParent, const std::wstring& name, bool wantToRename);
        bool validateFamilyTypeName(const std::wstring&);
        void onFamilyTypeNameChanged(const std::wstring&);
        QtNameFamilyType* GetQtNameFamilyType() { return m_qtNameFamilyType; }
    private:
        gcmp::IDocument* GetDocument();
        QtNameFamilyType* m_qtNameFamilyType;
        FamilyTypeNameComponentParent* m_parent;
    };

    class FamilyNameValuePairsComponent : public PropertyNameValuePairsComponent, public FamilyTypeNameComponentParent
    {
    public:
        FamilyNameValuePairsComponent(const std::wstring& title);

        virtual bool UpdatePropertyItemToElement(gnuf::Item* pItem) override;
        virtual bool UpdatePropertyTableToElement() override;
        virtual void onFamilyTypeNameChanged(const std::wstring&) override;
        virtual void BuildNameValuePairs(bool rebuild = true, bool fromElements = true);
        virtual bool UpdatePropertyTableToFamily();
        bool UpdateParameterFromItem(gcmp::IParameter* param, gnuf::Item* pItem);
        void UpdateParameterOnTable();

        void onEditTypeDialogOkBtnClicked();
        void onEditTypeDialogCancelClicked();

        void quitTransaction();

        FamilyTypeNameComponent* GetFamilyTypeNameComponent() { return m_familyNameComponent.get(); }
        QtEditFamilyType* GetQtEditFamilyType() { return m_editFamilyType; }

        virtual void onClicked(gnuf::Item*) override;
        void OnPropertyBindButtonClicked(gnuf::Item*);

    private:
        virtual void onCurrentIndexChanged(gnuf::List*, int) override;
        void UpdateViewVisual() const;
        gcmp::ElementId GetCurFamilyTypeId();
    private:
        gcmp::OwnerPtr<FamilyTypeNameComponent> m_familyNameComponent;
        QtEditFamilyType* m_editFamilyType;
        gcmp::OwnerPtr<gcmp::IUserTransaction> m_upUserTransaction;
        std::vector<gcmp::OwnerPtr<gcmp::IParameter>> m_vecDisplayedParameters;
        std::vector<int> m_modifiedTypeParamIds;
        std::vector<size_t> m_vecParametersOrder;
    };

    class FamilyTypeIntegratedComponent : public gnuf::IItemEventHandler, public FamilyTypeNameComponentParent
    {
        friend class GnufPropertyPallet;
    public:
        FamilyTypeIntegratedComponent();
        void BuildFamilyTypeComponent();
        void SetElementIds(const std::vector<gcmp::ElementId>& ids) { m_elementIds = ids; }
        void SetFamilyTypes(gnuf::FamilyTypes* pFamilyTypes) { m_pFamilyTypes = pFamilyTypes; }
        void SetDocument(gcmp::IDocument* pDoc);
        gnuf::FamilyTypes* GetFamilyTypes() const { return m_pFamilyTypes; }

        virtual void onAddFamilyType(gnuf::FamilyType*) override;
        virtual void onEditFamilyType(gnuf::FamilyType*) override;
        virtual void onCurrentFamilyTypeChanged(gnuf::FamilyTypes *, gnuf::FamilyType *, gnuf::FamilyType *) override;
        virtual void onFamilyTypeNameChanged(const std::wstring&) override;

        void refreshAppearance();

        void quitTransaction();

    private:
        void ShowViewComponentType() const;
        void SetStatusOfAddAndModifyButton();

        gnuf::FamilyTypes* m_pFamilyTypes;
        std::vector<gcmp::ElementId> m_elementIds;
        gcmp::IDocument* m_pDoc;
        gcmp::OwnerPtr<gcmp::IUserTransaction> m_upUserTransaction;
        gcmp::OwnerPtr<FamilyNameValuePairsComponent> m_familyNameValuePairsBuilder;
        gcmp::OwnerPtr<FamilyTypeNameComponent> m_familyNameComponent;
    };
    
    class COMPONENT_EDITOR_GNUF_UI_EXPORT GnufPropertyPallet final
        : public gnuf::ArrangementAreaContent
        , public gfam::IGnufPropertyWidget
    {
        friend class PropertyPalletUiViewEventHandler;
    public:
        GnufPropertyPallet();
        ~GnufPropertyPallet();

        InstanceNameValuePairsComponent* GetInstanceNameValuePairsBuilder();
        FamilyTypeIntegratedComponent* GetFamilyTypeComponentBuilder();
        QtGnufPropertyPallet* GetQtGnufPropertyPallet() { return m_qtGnufPropertyPallet.get(); }
        static GnufPropertyPallet* Get();
        static void Initialize();
        virtual void Refresh();
        virtual void SetDefaultElementId(const gcmp::ElementId& idDefaultElement);
        virtual const gcmp::ElementId GetDefaultElementId() const;
        virtual void SetPreSetElementId(const gcmp::ElementId& elementId);
        virtual const gcmp::ElementId GetPreSetElementId() const;
        virtual IGnufPropertyPanelEvent* GetInputEvent();
        virtual void SetEnabled(bool bEnable);
        virtual std::wstring GetComponentTypeChangedEventId() const;
        virtual std::wstring GetComponentTypeModifiedPropertyEventId() const;
        virtual std::wstring GetInstancePropertyItemChangedEventId() const;
        virtual bool isVisible() const;
        virtual void setVisible(bool isVisible);
        virtual IGnufPropertyPanelEvent* GetInputFinishedEvent();

        virtual QWidget * qtWidget() override;
        const QWidget * qtWidget() const;

        virtual void onPostGeometryChanged() override;
        virtual void onPostVisibilityChanged() override;

        gcmp::IDocument* GetDocument() const;
        void CheckDocSwitched();
        void GetShowElements(std::vector<gcmp::ElementId>& selElements) const;
        bool IsVisible() const;
        bool IsEnable() const;
        std::vector<gcmp::ElementId> GetSelectedElements() const;
        std::vector<gcmp::ElementId> GetSourceElements() const;
        bool HasSelectedElements();

        void quitTransaction();

    protected:
        virtual void onPostConnectedWithArrangementArea() override;
        virtual void onPreDisconnectedFromArrangementArea() override;
        virtual void onEvent(gnuf::ArrangementAreaEvent*) override;

    private:
        void AttachElementChangeObserver(gcmp::IUiView* pUIView);
        gcmp::IUiDocumentEvent* GetUiDocumentEvent() const;
        gcmp::IUiViewEvent* GetUiViewEvent() const;
        void InitContent();

    private:
        gcmp::OwnerPtr< QtGnufPropertyPallet > m_qtGnufPropertyPallet;
        gcmp::OwnerPtr< gnuf::Pallet > m_pallet;

        Int64   m_endOfTransactionProcessorId;

        gcmp::OwnerPtr<gcmp::IElementsChangeEventHandler>    m_elementChangeObserver;       //有对象改变（监听）
        gcmp::OwnerPtr<gcmp::IUiDocumentEventHandler>        m_uiDocumentEventHandler;      //UiDoc事件（监听）
        gcmp::OwnerPtr<gcmp::IUiViewEventHandler>            m_uiViewEventHandler;          //UiView事件（监听）
        gcmp::OwnerPtr<gcmp::ISelectionChangeEventHandler>   m_selectionChangeEventHandler; //选择集改变（监听）
        gcmp::OwnerPtr<IGnufPropertyPanelEvent>              m_inputEvent;                  //用户修改属性表事件(对外通知）  
        gcmp::OwnerPtr<IGnufPropertyPanelEvent>              m_inputFinishedEvent;          //用户修改属性表完成后事件(对外通知）
        gcmp::ElementId m_defaultElementId;
        gcmp::ElementId m_tmpPreSetElementId;        // 用于预设参数的临时构件
        gcmp::RuntimeId m_curUiDocId;
    };

}